package com.sparrow.common.faketime;

import com.sparrow.common.util.TimeUtil;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;

/**
 * 伪造时间 辅助类
 * # if you're on Windows
 * -agentpath:target/faketime.dll
 * -XX:+UnlockDiagnosticVMOptions
 * -XX:DisableIntrinsic=_currentTimeMillis
 * -XX:CompileCommand=quiet
 * -XX:CompileCommand=exclude,java/lang/System.currentTimeMillis
 * -XX:CompileCommand=exclude,jdk/internal/misc/VM.getNanoTimeAdjustment
 *
 * # if you're on macOS/Linux
 * -agentpath:target/libfaketime
 * -XX:+UnlockDiagnosticVMOptions
 * -XX:DisableIntrinsic=_currentTimeMillis
 * -XX:CompileCommand=quiet
 * -XX:CompileCommand=exclude,java/lang/System.currentTimeMillis
 * -XX:CompileCommand=exclude,jdk/internal/misc/VM.getNanoTimeAdjustment
 */
public class FakeTimeHelper {
	/**
	 * 是否启用 faketime
	 */
	public static final String FAKE_TIME_OPEN_PROPERTY = "faketime.open";
	/**
	 * 偏移时间（毫秒）
	 */
	public static final String FAKE_TIME_OFFSET_PROPERTY = "faketime.offset.ms";

	private static final String OFFSET_FILE = "faketime/offset";

	/**
	 * 起服之前恢复之前时间偏移
	 */
	public static void init() {
		if ("true".equals(System.getProperty(FAKE_TIME_OPEN_PROPERTY, "false"))) {
			File fakeTimeOffset = new File(OFFSET_FILE);
			if (fakeTimeOffset.exists()) {
				try {
					byte[] bytes = Files.readAllBytes(fakeTimeOffset.toPath());
					String offsetValue = new String(bytes);
					if (StringUtils.equals(offsetValue, System.getProperty(FAKE_TIME_OFFSET_PROPERTY))) {
						return;
					}
					long offset = Long.parseLong(offsetValue);
					System.setProperty(FAKE_TIME_OFFSET_PROPERTY, String.valueOf(offset));
				} catch (Exception exception) {
					throw new RuntimeException("fakeTime时间偏移恢复异常", exception);
				}
			}
		}
	}

	/**
	 * 修改 jvm 时间
	 * @param time 时间格式，yyyy-MM-dd HH:mm:ss
	 */
	public static synchronized void fakeTime(String time) {
		long fakeTime = TimeUtil.parse(time);
		String offsetProp = System.getProperty(FAKE_TIME_OFFSET_PROPERTY);
		long offset;
		if (offsetProp != null) {
			// 根据 offset 算出真实时间
			long realTime = System.currentTimeMillis() - Long.parseLong(offsetProp);
			offset = fakeTime - realTime;
		} else {
			offset = fakeTime - System.currentTimeMillis();
		}
		System.setProperty(FAKE_TIME_OFFSET_PROPERTY, Long.toString(offset));
		save();
	}

	/**
	 * 保存当前偏移
	 */
	private static void save() {
		File fakeTimeOffset = new File(OFFSET_FILE);
		if (!fakeTimeOffset.exists()) {
			try {
				Files.createDirectories(fakeTimeOffset.toPath().getParent());
			} catch (Exception e) {
				throw new RuntimeException("fakeTime创建目录异常", e);
			}
		}
		String property = System.getProperty(FAKE_TIME_OFFSET_PROPERTY);
		if (StringUtils.isEmpty(property)) {
			//noinspection ResultOfMethodCallIgnored
			fakeTimeOffset.delete();
		} else {
			try {
				Files.write(fakeTimeOffset.toPath(), property.getBytes(StandardCharsets.UTF_8));
			} catch (Exception exception) {
				throw new RuntimeException("fakeTime保存时间偏移异常", exception);
			}
		}
	}

}
